<?php
// +----------------------------------------------------------------------------
// | Tke团队版权所属 [三十年河东三十年河西,莫欺少年穷.!]
// +----------------------------------------------------------------------------
// | Copyright (c) 2014 http://www.itdaodan.com All rights reserved.
// +----------------------------------------------------------------------------
// | Author: PHP@妖孽 <dino_ma@163.com>
// +----------------------------------------------------------------------------
/**
 +------------------------------------------------------------------------------
 * 支付宝支付驱动
 +------------------------------------------------------------------------------
 * @version   1.0
 * @since 2014-12-11
 +------------------------------------------------------------------------------
 */
namespace Think\Pay\Driver;

class Alipay extends \Think\Pay\Pay {

	protected $gateway = 'https://mapi.alipay.com/gateway.do';
	protected $verify_url = 'http://notify.alipay.com/trade/notify_query.do';
	protected $get_order_url = 'http://mapi.alimock.com/alipay/gateway.h4';
	protected $config = array(
			'email' => '',
			'key' => '',
			'partner' => ''
	);
	
	public function check() {
		if (!$this->config['email'] || !$this->config['key'] || !$this->config['partner'] || !$this->config['app_id']) {
			E("支付宝设置有误！");
		}
		return true;
	}

	public function buildRequestForm(\Think\Pay\PayVo $vo) {
		$param = array(
				'service' => 'create_direct_pay_by_user',
				'payment_type' => '1',
				'_input_charset' => 'utf-8',
				'seller_email' => $this->config['email'],
				'partner' => $this->config['partner'],
				'notify_url' => $this->config['notify_url'],
				'return_url' => $this->config['return_url'],
				'out_trade_no' => $vo->getOrderNo(),
				'subject' => $vo->getTitle(),
				'body' => $vo->getBody(),
				'total_fee' => $vo->getFee()
		);

		ksort($param);
		reset($param);

		$arg = '';
		foreach ($param as $key => $value) {
			if ($value) {
				$arg .= "$key=$value&";
			}
		}

		$param['sign'] = md5(substr($arg, 0, -1) . $this->config['key']);
		$param['sign_type'] = 'MD5';
		$sHtml = $this->gateway.'?'.http_build_query($param);
		return $sHtml;
// 		$sHtml = $this->_buildForm($param, $this->gateway, 'get');

// 		return $sHtml;
	}

	/**
	 * 获取返回时的签名验证结果
	 * @param $para_temp 通知返回来的参数数组
	 * @param $sign 返回的签名结果
	 * @return 签名验证结果
	 */
	protected function getSignVeryfy($param, $sign) {
		//除去待签名参数数组中的空值和签名参数
		$param_filter = array();
		while (list ($key, $val) = each($param)) {
			if ($key == "sign" || $key == "sign_type" || $val == "") {
				continue;
			} else {
				$param_filter[$key] = $param[$key];
			}
		}

		ksort($param_filter);
		reset($param_filter);

		//把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		$prestr = "";
		while (list ($key, $val) = each($param_filter)) {
			$prestr.=$key . "=" . $val . "&";
		}
		//去掉最后一个&字符
		$prestr = substr($prestr, 0, -1);

		$prestr = $prestr . $this->config['key'];
		$mysgin = md5($prestr);

		if ($mysgin == $sign) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 针对notify_url验证消息是否是支付宝发出的合法消息
	 * @return 验证结果
	 */
	public function verifyNotify($notify) {

		//生成签名结果
		$isSign = $this->getSignVeryfy($notify, $notify["sign"]);
		//获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
		$responseTxt = 'true';
		if (!empty($notify["notify_id"])) {
			$responseTxt = $this->getResponse($notify["notify_id"]);
		}

		if (preg_match("/true$/i", $responseTxt) && $isSign) {
			$this->setInfo($notify);
			return true;
		} else {
			return false;
		}
	}

	protected function setInfo($notify) {
		$info = array();
		//支付状态
		$info['status'] = ($notify['trade_status'] == 'TRADE_FINISHED' || $notify['trade_status'] == 'TRADE_SUCCESS') ? true : false;
		$info['money'] = $notify['total_fee'];
		$info['out_trade_no'] = $notify['out_trade_no'];
		$info['account_num']	= $notify['buyer_email'];
		$this->info = $info;
	}

	/**
	 * 获取远程服务器ATN结果,验证返回URL
	 * @param $notify_id 通知校验ID
	 * @return 服务器ATN结果
	 * 验证结果集：
	 * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
	 * true 返回正确信息
	 * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	protected function getResponse($notify_id) {
		$partner = $this->config['partner'];
		$veryfy_url = $this->verify_url . "?partner=" . $partner . "&notify_id=" . $notify_id;
		$responseTxt = $this->fsockOpen($veryfy_url);
		return $responseTxt;
	}
	
	
	//-----订单查询rsa加密－－－－－－／／
	/**
	 * @todo 查询订单状态
	 * @param \Think\Pay\PayVo $vo
	 */
	public function getOrderQuery(\Think\Pay\PayVo $vo) {
	    //构造请求参数。
	    $param = array(
	        'app_id' => $this->config['app_id'],
	        'out_trade_no' => $vo->getOrderNo(),
	        'format'=>'json',
	        'sign_type'=>'RSA',//签名方式。
	        'method'=>'alipay.trade.query',
	        'alipay_sdk'=>'alipay-sdk-php-20130320',
	        'notify_url'=>$this->config['notify_url'],
	        'charset'=>'UTF-8',
	        'timestamp'=>date('Y-m-d H:is'),
	        'app_auth_token'=>'',
	        'terminal_type'=>'',
	        'terminal_info'=>'',
	        'prod_code'=>''
	    );
	    $param['sign'] = $this->rsaSign($param);
	  
	    
	   
	    
	    
	    //发起HTTP请求
	    try {
	        $resp = $this->curl($this->get_order_url, $param);
	    } catch (\Exception $e) {
	        E($param["method"]. $this->get_order_url."HTTP_ERROR_" . $e->getCode().$e->getMessage());
	        return false;
	    }
	    // 将返回结果转换本地文件编码
	    $r = iconv($this->postCharset, $this->fileCharset . "//IGNORE", $resp);
	    $call_back = json_decode($resp,true);
	    if(empty($call_back)){
	        E('请求失败');
	    }
	    $response = $call_back['alipay_trade_query_response'];
	    if($response['code']== 10000){
	        //请求成功
	        if($response['trade_status']=='TRADE_SUCCESS'){
	            //交易成功
	            return array(
	                'out_trade_no' => $response['out_trade_no'], //支付网关订单号
	                'transaction_id' => $response['trade_no'],//交易号
	                'attach'=>array(),//附加参数
	                'money'=>$response['total_amount'],//交易金额
	            );
	        }else{
	            //交易失败
	            E('此订单交易未成功');
	        }
	    }else{
	        //请求失败
	        E($response['sub_desc']);
	    }
	}
    
	/**
	 * @todo curl
	 * @param string $url
	 * @param array $postFields
	 * @throws \Exception
	 */
	protected function curl($url, $postFields = null) {
	    $ch = curl_init();
	    curl_setopt($ch, CURLOPT_URL, $url);
	    curl_setopt($ch, CURLOPT_FAILONERROR, false);
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	
	    $postBodyString = "";
	    $encodeArray = Array();
	    $postMultipart = false;
	
	    if (is_array($postFields) && 0 < count($postFields)) {
	
	
	        foreach ($postFields as $k => $v) {
	            if ("@" != substr($v, 0, 1)) //判断是不是文件上传
	            {
	
	                $postBodyString .= "$k=" . urlencode($this->characet($v, $this->postCharset)) . "&";
	                $encodeArray[$k] = $this->characet($v, $this->postCharset);
	            } else //文件上传用multipart/form-data，否则用www-form-urlencoded
	            {
	                $postMultipart = true;
	                $encodeArray[$k] = new \CURLFile(substr($v,1));
	            }
	
	        }
	        unset ($k, $v);
	        curl_setopt($ch, CURLOPT_POST, true);
	        if ($postMultipart) {
	            curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
	        }else{
	            curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
	        }
	    }
	
	    if($postMultipart){
	
	        $headers = array('content-type: multipart/form-data;charset=' . $this->postCharset.';boundary='.$this->getMillisecond());
	    }else{
	
	        $headers = array('content-type: application/x-www-form-urlencoded;charset=' . $this->postCharset);
	    }
	    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
	
	
	
	    $reponse = curl_exec($ch);
	
	    if (curl_errno($ch)) {
	        throw new \Exception(curl_error($ch), 0);
	    } else {
	        $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	        if (200 !== $httpStatusCode) {
	            throw new \Exception($reponse, $httpStatusCode);
	        }
	    }
	    curl_close($ch);
	    return $reponse;
	}
	
	/**
	 * @todo rsa 加密
	 * @param array $params
	 */
	public function rsaSign($params) {
	    return $this->sign($this->getSignContent($params));
	}
	
	protected function getSignContent($params) {
	    ksort($params);
	
	    $stringToBeSigned = "";
	    $i = 0;
	    foreach ($params as $k => $v) {
	        if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
	
	            // 转换成目标字符集
	            $v = $this->characet($v, $this->postCharset);
	
	            if ($i == 0) {
	                $stringToBeSigned .= "$k" . "=" . "$v";
	            } else {
	                $stringToBeSigned .= "&" . "$k" . "=" . "$v";
	            }
	            $i++;
	        }
	    }
	
	    unset ($k, $v);
	    return $stringToBeSigned;
	}
	
	protected function sign($data) {
// $this->config['merchant_private_key_file']='/Users/mashengjie/Downloads/rsa_private_key.pem';
	    $priKey = file_get_contents($this->config['merchant_private_key_file']);
	    $res = openssl_get_privatekey($priKey);
	    openssl_sign($data, $sign, $res);
	    openssl_free_key($res);
	    $sign = base64_encode($sign);
	    return $sign;
	}
	
	/**
	 * 校验$value是否非空
	 *  if not set ,return true;
	 *    if is null , return true;
	 **/
	protected function checkEmpty($value) {
	    if (!isset($value))
	        return true;
	        if ($value === null)
	            return true;
	            if (trim($value) === "")
	                return true;
	
	                return false;
	}
	
	/**
	 * 转换字符集编码
	 * @param $data
	 * @param $targetCharset
	 * @return string
	 */
	function characet($data, $targetCharset) {
	    if (!empty($data)) {
	        $fileType = 'UTF-8';
	        if (strcasecmp($fileType, $targetCharset) != 0) {
	            $data = mb_convert_encoding($data, $targetCharset);
	            //				$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
	        }
	    }
	    return $data;
	}
}